use std::ffi::CStr;
use std::mem;
use winapi::shared::minwindef::{BOOL, BYTE, DWORD, HMODULE};
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcess};
use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, LPMODULEENTRY32, Module32First, Module32Next, MODULEENTRY32};
use winapi::um::winnt::{HANDLE, MEMORY_BASIC_INFORMATION, PROCESS_ALL_ACCESS, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, PROCESS_VM_WRITE};
use winapi::um::memoryapi::{ReadProcessMemory, VirtualQueryEx, WriteProcessMemory};
use regex::Regex;

#[allow(dead_code)]
#[derive(Debug)]
pub struct DebugModuleentry32 {
    dw_size: DWORD,
    th32module_id: DWORD,
    th32process_id: DWORD,
    glblcnt_usage: DWORD,
    proccnt_usage: DWORD,
    mod_base_addr: *mut BYTE,
    mod_base_size: DWORD,
    h_module: HMODULE,
    sz_module: String,
    sz_exe_path: String,
}

pub unsafe fn create_toolhelp32snapshot(falg: u32, id: u32) -> HANDLE {
    CreateToolhelp32Snapshot(falg, id)
}

pub unsafe fn module32first(h_snapshot: HANDLE, lpme: LPMODULEENTRY32) -> BOOL {
    Module32First(h_snapshot, lpme)
}

pub unsafe fn module32next(h_snapshot: HANDLE, lpme: LPMODULEENTRY32) -> BOOL {
    Module32Next(h_snapshot, lpme)
}

pub unsafe fn close_handle(h_object: HANDLE) -> BOOL {
    CloseHandle(h_object)
}

#[allow(dead_code)]
pub unsafe fn get_module_list(pid: u32) -> Vec<DebugModuleentry32> {
    let mut result: Vec<DebugModuleentry32> = Vec::new();
    let snapshot_handle = create_toolhelp32snapshot(8, pid);
    let mut module_inif: MODULEENTRY32 = std::mem::zeroed();
    module_inif.dwSize = 1024;
    let mut process_handle = module32first(snapshot_handle, &mut module_inif);
    while process_handle != 0 {
        result.push(DebugModuleentry32 {
            dw_size: module_inif.dwSize,
            th32module_id: module_inif.th32ModuleID,
            th32process_id: module_inif.th32ProcessID,
            glblcnt_usage: module_inif.GlblcntUsage,
            proccnt_usage: module_inif.ProccntUsage,
            mod_base_addr: module_inif.modBaseAddr,
            mod_base_size: module_inif.modBaseSize,
            h_module: module_inif.hModule,
            sz_module: String::from(CStr::from_ptr(module_inif.szModule.as_ptr()).to_str().unwrap()),
            sz_exe_path: String::from(CStr::from_ptr(module_inif.szExePath.as_ptr()).to_str().unwrap()),
        });
        process_handle = module32next(snapshot_handle, &mut module_inif);
    }
    close_handle(snapshot_handle);
    result
}

pub unsafe fn get_specified_module_handle(pid: u32, module_name: String) -> HMODULE {
    let mut result: HMODULE = 0 as HMODULE;
    let snapshot_handle = create_toolhelp32snapshot(8, pid);
    let mut module_inif: MODULEENTRY32 = std::mem::zeroed();
    module_inif.dwSize = 1024;
    let mut process_handle = module32first(snapshot_handle, &mut module_inif);
    while process_handle != 0 {
        if module_name == String::from(CStr::from_ptr(module_inif.szModule.as_ptr()).to_str().unwrap()) {
            result = module_inif.hModule;
            break;
        }
        process_handle = module32next(snapshot_handle, &mut module_inif);
    }
    close_handle(snapshot_handle);
    result
}

pub unsafe fn read_memory_int(process_id: i32, address: u64) -> Result<i32, i32> {
    let mut buffer = [0u8; 4];
    let process_handle = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id as DWORD);
    if process_handle.is_null() {
        return Err(-1);
    }
    let mut bytes_read = 0;
    if ReadProcessMemory(process_handle, address as *const _, buffer.as_mut_ptr() as *mut _, buffer.len(), &mut bytes_read) == 0 {
        return Err(-1);
    }
    let num = i32::from_le_bytes(buffer);
    Ok(num)
}

pub unsafe fn read_memory(process_id: i32, address: u64, length: i32) -> String {
    let mut buffer = vec![0; length as usize];
    let process_handle = get_process_handle(process_id, PROCESS_VM_READ);
    if process_handle.is_null() {
        return String::new();
    }
    let mut bytes_read = 0;
    if length == 0 {
        let mut mem_info = mem::zeroed();
        VirtualQueryEx(process_handle, address as *const _, &mut mem_info, mem::size_of::<MEMORY_BASIC_INFORMATION>());
        buffer.resize(mem_info.RegionSize as usize, 0);
    }
    if ReadProcessMemory(process_handle, address as *const _, buffer.as_mut_ptr() as *mut _, buffer.len(), &mut bytes_read) == 0 {
        buffer = vec![];
    } else {
        buffer.resize(bytes_read, 0);
    }
    CloseHandle(process_handle);
    // 编码处理
    let re = Regex::new(r"[\p{Han}\p{P}\p{S}\p{L}\p{M}\p{Z}\d]+").unwrap();
    if let Some(captures) = re.captures(&*String::from_utf8_lossy(&buffer).replace("\0", "")) {
        captures.get(0).unwrap().as_str().split('�').next().unwrap().to_string()
    } else {
        "".to_string()
    }
}

pub unsafe fn read_memory_byte(process_id: i32, address: u64) -> Result<u8, u8> {
    let mut buffer = [0u8; 1];
    let process_handle = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id as DWORD);
    if process_handle.is_null() {
        return Err(0);
    }
    let mut bytes_read = 0;
    if ReadProcessMemory(process_handle, address as *const _, buffer.as_mut_ptr() as *mut _, buffer.len(), &mut bytes_read) == 0 {
        return Err(0);
    }
    let num = buffer[0];
    Ok(num)
}

#[allow(dead_code)]
pub unsafe fn write_memory(process_id: i32, address: usize, data: &[u8]) -> bool {
    let process_handle = get_process_handle(process_id, PROCESS_VM_WRITE);
    if process_handle.is_null() {
        return false;
    }
    let mut bytes_written = 0;
    unsafe {
        if WriteProcessMemory(process_handle, address as *mut _, data.as_ptr() as *const _, data.len(), &mut bytes_written) == 0 {
            CloseHandle(process_handle);
            return false;
        }
        CloseHandle(process_handle);
    }
    bytes_written == data.len()
}

pub unsafe fn get_process_handle(process_id: i32, access: u32) -> HANDLE {
    if process_id == -1 {
        unsafe { GetCurrentProcess() }
    } else {
        unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | access, 0, process_id as u32) }
    }
}

pub fn hex_to_decimal(hex: &str) -> u64 {
    let mut result: u64 = 0;
    for c in hex.chars() {
        let digit: u64 = match c.to_digit(16) {
            Some(d) => d as u64,
            None => panic!("Invalid hex digit: {}", c),
        };
        result = result * 16 + digit;
    }
    result
}